﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CivLib.CivHyd;

namespace CivLib.CivNum
{
  /// <summary>
  /// Class that is Orifice Equation
  /// </summary>
  public class Orifice : EquationBase, IOutlet
  {
    #region contained vars
    /// <summary>
    /// Enumeration of the variable indexes
    /// </summary>
    public enum VarI
    {
      /// <summary>Flow [cfs] [cms]</summary>
      Q = 0,
      /// <summary>Coefficent of Discharge</summary>
      Cd = 1,
      /// <summary>Depth at center of orifice [ft] [m]</summary>
      h = 2
    };
    CivHyd.IHydraulicXsec m_orifice;
    #endregion

    #region Parameters
    /// <summary>Flow [cfs] [cms]</summary>
    public double Q
    {
      get { return vars[0];}
      set { vars[0] = value;}
    }

    /// <summary>Coefficent of Discharge</summary>
    public double Cd
    {
      get { return vars[1];}
      set { vars[1] = value;}
    }

    /// <summary>Depth at center of orifice [ft] [m]</summary>
    public double h
    {
      get { return vars[2];}
      set { vars[2] = value;}
    }
    #endregion

    #region constructors
    /// <summary>
    /// Constructs a standard orfice with a circle for the opening and 
    /// a radius of 1
    /// </summary>
    public Orifice() :
      base("Orifice Equation", 3,
      new String[] { "Flow", "Orifice Coefficent", "Depth to Centerline" })
    {
      m_orifice = new CivSpatial.Circle(1);
      Cd = 0.62d;
    }

    /// <summary>
    /// Constructor for orifice given the shape of the outlet
    /// </summary>
    /// <param name="a_orifice">Xsec defining the outlet shape</param>
    public Orifice(CivHyd.IHydraulicXsec a_orifice) :
      base("Orifice Equation", 3,
      new String[] { "Flow", "Orifice Coefficent", "Depth to Centerline" })
    {
      m_orifice = a_orifice;
      Cd = 0.62d;
    }
    #endregion

    #region ISoluableEquation
    /// <summary>
    /// Current error in the function.  When this reaches 0 the
    /// variables can be considered valid.
    /// </summary>
    /// <returns>the current error</returns>
    public override double Residual()
    {
      return Cd * m_orifice.Area() * Math.Sqrt(2 * Const.g * h) - Q;
    }
    #endregion

    /// <summary>
    /// Solve for specified VarI enumeration
    /// </summary>
    /// <param name="i">VarI enumeration of variable</param>
    /// <returns></returns>
    public double SolveFor(VarI i)
    {
      switch (i)
      {
        case VarI.Cd:
          {
            Cd = Q / (m_orifice.Area() * Math.Sqrt(2 * Const.g * h));
          }break;
        case VarI.h:
          {
            h = Math.Pow(Q / (Cd * m_orifice.Area()), 2) / (2 * Const.g);
          }break;
        case VarI.Q:
          {
            Q = Cd * m_orifice.Area() * Math.Sqrt(2 * Const.g * h);
          }break;
      }
      return vars[(int)i];
    }

    /// <summary>
    /// Gets flow given specified depth
    /// </summary>
    /// <param name="a_depth">depth of flow</param>
    /// <returns>the flow [cfs] [cms]</returns>
    public double FlowAtDepth(double a_depth)
    {
      h = a_depth;
      return SolveFor(VarI.Q);
    }
  }

  /// <summary>
  /// Class that is Mannings Equation.
  /// </summary>
    public class Manning : EquationBase
    {
      #region Constained Vars
      /// <summary>
      /// Convinence enumeration for listing the positions of the variables
      /// in the VarVec
      /// </summary>
      public enum VarI
      {
        /// <summary>
        /// Flow [cfs] [cms]
        /// </summary>
        Q = 0,
        /// <summary>
        /// Manning's n
        /// </summary>
        n = 1,
        /// <summary>
        /// Depth of flow [ft] [m]
        /// </summary>
        y = 2,
        /// <summary>
        /// Slope of channel [ft/ft] [m/m]
        /// </summary>
        S = 3
      };
      NewtonRaphson theSolver;
      CivHyd.IHydraulicXsec m_Xsec;
      #endregion

      #region parameters
      CivHyd.IHydraulicXsec XSection
      {
        get { return m_Xsec; }
        set { m_Xsec = value; }
      }
      /// <summary>
      /// Flow
      /// </summary>
      public double Q
      {
        get { return vars[0]; }
        set { vars[0] = value; }
      }
      /// <summary>
      /// Manning's Roughness
      /// </summary>
      public double n
      {
        get { return vars[1]; }
        set { vars[1] = value; }
      }
      /// <summary>
      /// Depth of the flow
      /// </summary>
      public double y
      {
        get { return vars[2]; }
        set { vars[2] = value; }
      }
      /// <summary>
      /// Slope
      /// </summary>
      public double S
      {
        get { return vars[3]; }
        set { vars[3] = value; }
      }
      #endregion

      #region constructors
      /// <summary>
      /// Constructor for a manning's equation.
      /// </summary>
      /// <param name="a_XSection">class that impliments IHydraulicXsec</param>
      public Manning(CivHyd.IHydraulicXsec a_XSection) :
        base("Manning's Equantion",
        4, new String[]{"Flow", "n", "Depth of Flow", "Slope"})
      {
        theSolver = new NewtonRaphson(this);
        m_Xsec = a_XSection;
      }
      #endregion

      #region ISoluableEqation
      /// <summary>
      /// The current residual of the function.  Function parameters are 
      /// valid when the residual is 0.
      /// </summary>
      /// <returns>Current Residual</returns>
      public override double Residual()
      {
        return (Const.Kn / n) * m_Xsec.PartialArea(y) *
          Math.Pow(HydMath.R(m_Xsec, y), Const.TwoThrids)
          * Math.Sqrt(S) - Q;
      }
      #endregion

      /// <summary>
      /// Gets the amount of flow if the cross section is flowing full.
      /// </summary>
      /// <returns>Q of full flow</returns>
      public double FlowingFull()
      {
        return (Const.Kn / n) * m_Xsec.Area() *
          Math.Pow(m_Xsec.Area()/m_Xsec.Perimeter(), Const.TwoThrids) *
          Math.Sqrt(S);
      }

      /// <summary>
      /// Solves for one of the variables
      /// </summary>
      /// <param name="i">Enum VarI of the variable 0-3</param>
      /// <returns>value of the variable after solution</returns>
      public double SolveFor(VarI i)
      {
        switch (i){
          case VarI.n:
            {
              n = (Const.Kn * m_Xsec.PartialArea(y) *
                Math.Pow(HydMath.R(m_Xsec, y), Const.TwoThrids) *
                Math.Pow(S, 0.5d)) / Q;
            }
            break;
          case VarI.Q:
            {
              Q = ((Const.Kn / n) * m_Xsec.PartialArea(y) *
                Math.Pow(HydMath.R(m_Xsec, y), Const.TwoThrids) *
                Math.Pow(S, 0.5d));
            }
            break;
          case VarI.S:
            {
              S = Math.Pow(Q / ((Const.Kn / n) * m_Xsec.PartialArea(y) *
                Math.Pow(HydMath.R(m_Xsec, y), Const.TwoThrids) *
                Math.Pow(S, 0.5d)), 2);
            }
            break;
          case VarI.y:
            {
              try
              {
                theSolver.Solve((int)i);
              }
              catch (SolverException e)
              {
                CivIO.CivConsole.Write(e.Message);
              }
            }
            break;
          default:
            {
              try
              {
                theSolver.Solve((int)i);
              }
              catch (SolverException e)
              {
                CivIO.CivConsole.Write(e.Message);
              }

            }
            break;
        }
        return vars[(int)i];
      }
    }

  /// <summary>
  /// Static class that defines basic hydraulic mathmatical equations
  /// </summary>
  public static class HydMath
  {
    /// <summary>
    /// Calculation of Hydraulic Radius for given X-Section
    /// </summary>
    /// <param name="theSection">object that implements IHydraulicXsec</param>
    /// <param name="theDepth">Depth of the flow</param>
    /// <returns>The Hydraulic Radius at the given depth</returns>
    public static double R(CivHyd.IHydraulicXsec theSection, double theDepth)
    {
      return theSection.PartialArea(theDepth) / theSection.PartialPerimiter(theDepth);
    }
  }
}
